﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Linq.Expressions;
using System.Data.Entity;

using HST.Domain.Seedwork;
using HST.Domain.Seedwork.Specifications;

namespace HST.Infrastructure.Data.Seedwork
{
    public class Repository<TAggregateRoot, TKey> : IRepository<TAggregateRoot, TKey>
        where TAggregateRoot : AggregateRoot<TKey>
        where TKey : IEquatable<TKey>
    {
        #region Members

        private IDatabaseFactory _databaseFactory;

        #endregion

        #region Constructor

        public Repository(IDatabaseFactory databaseFactory)
        {
            if (databaseFactory == (IUnitOfWork)null)
            {
                throw new ArgumentNullException("IDatabaseFactory");
            }

            _databaseFactory = databaseFactory;
        }

        #endregion

        #region Private Methods

        IDbSet<TAggregateRoot> GetSet()
        {
            return _databaseFactory.Create().CreateSet<TAggregateRoot>();
        }

        #endregion

        #region IRepository Members

        public IUnitOfWork UnitOfWork
        {
            get
            {
                return _databaseFactory.Create();
            }
        }

        public virtual void Add(TAggregateRoot aggregateRoot)
        {
            if (aggregateRoot != (TAggregateRoot)null)
            {
                GetSet().Add(aggregateRoot);
            }
            else
            {
                //LoggerFactory.CreateLog()
                //          .LogInfo(Messages.info_CannotAddNullEntity, typeof(TEntity).ToString());
            }
        }

        public virtual void Remove(TAggregateRoot aggregateRoot)
        {
            if (aggregateRoot != (TAggregateRoot)null)
            {
                GetSet().Remove(aggregateRoot);
            }
            else
            {
                // TODO:输出日志
            }
        }

        /// <summary>
        /// Delete all elements of type TAggregateRoot in repository
        /// </summary>
        /// <param name="filter">Filter that each element do match</param>
        public void Remove(Expression<Func<TAggregateRoot, bool>> filter)
        {
            IEnumerable<TAggregateRoot> aggregateRoots = GetSet().Where<TAggregateRoot>(filter).AsEnumerable();
            foreach (TAggregateRoot aggregateRoot in aggregateRoots)
            {
                Remove(aggregateRoot);
            }
        }

        /// <summary>
        /// Delete all elements of type TAggregateRoot in repository
        /// </summary>
        /// <param name="specification">Specified that each element do match</param>
        public void Remove(ISpecification<TAggregateRoot> specification)
        {
            IEnumerable<TAggregateRoot> aggregateRoots = GetSet().Where(specification.SatisfiedBy());
        }


        public virtual void Modify(TAggregateRoot aggregateRoot)
        {
            if (aggregateRoot != (TAggregateRoot)null)
            {
                _databaseFactory.Create().SetModified(aggregateRoot);
            }
            else
            {
                // TODO:输出日志
            }
        }

        public virtual TAggregateRoot GetSingle(Expression<Func<TAggregateRoot, bool>> filter)
        {
            return GetSet().Where<TAggregateRoot>(filter).SingleOrDefault();
        }

        public TAggregateRoot GetSingle(ISpecification<TAggregateRoot> specification)
        {
            return GetSet().Where<TAggregateRoot>(specification.SatisfiedBy()).SingleOrDefault();
        }

        public virtual IEnumerable<TAggregateRoot> GetAll()
        {
            return GetSet();
        }

        /// <summary>
        /// <see cref="HST.Domain.Seedwork.IRepository{TValueObject}"/>
        /// </summary>
        /// <typeparam name="S"><see cref="HST.Domain.Seedwork.IRepository{TValueObject}"/></typeparam>
        /// <param name="pageIndex"><see cref="HST.Domain.Seedwork.IRepository{TValueObject}"/></param>
        /// <param name="pageSize"><see cref="HST.Domain.Seedwork.IRepository{TValueObject}"/></param>
        /// <param name="orderByExpression"><see cref="HST.Domain.Seedwork.IRepository{TValueObject}"/></param>
        /// <param name="order">Order by</param>
        /// <returns><see cref="HST.Domain.Seedwork.IRepository{TValueObject}"/></returns>
        public virtual IEnumerable<TAggregateRoot> GetAllPaged<KProperty>(Expression<Func<TAggregateRoot, KProperty>> orderByExpression, HST.Domain.Seedwork.Enumerators.Order order, int pageIndex, int pageSize, out int totalRecords)
        {
            var dbset = GetSet();
            totalRecords = dbset.Count();

            switch (order)
            {
                case Domain.Seedwork.Enumerators.Order.Ascending:
                    return dbset.OrderBy(orderByExpression)
                          .Skip(pageSize * pageIndex)
                          .Take(pageSize);

                case Domain.Seedwork.Enumerators.Order.Descending:
                    return dbset.OrderByDescending(orderByExpression)
                          .Skip(pageSize * pageIndex)
                          .Take(pageSize);

                default:
                    return dbset.Skip(pageSize * pageIndex)
                          .Take(pageSize);
            }
        }

        /// <summary>
        /// <see cref="HST.Domain.Seedwork.IRepository{TValueObject}"/>
        /// </summary>
        /// <param name="filter"><see cref="HST.Domain.Seedwork.IRepository{TValueObject}"/></param>
        /// <returns><see cref="HST.Domain.Seedwork.IRepository{TValueObject}"/></returns>
        public virtual IEnumerable<TAggregateRoot> GetFiltered(Expression<Func<TAggregateRoot, bool>> filter)
        {
            return GetSet().Where(filter);
        }

        public IEnumerable<TAggregateRoot> GetFiltered(ISpecification<TAggregateRoot> specification)
        {
            return GetSet().Where(specification.SatisfiedBy());
        }

        public IEnumerable<TAggregateRoot> GetFilterPaged<KProperty>(Expression<Func<TAggregateRoot, bool>> filter, Expression<Func<TAggregateRoot, KProperty>> orderByExpression, HST.Domain.Seedwork.Enumerators.Order order, int pageIndex, int pageSize, out int totalRecords)
        {
            var dbset = GetSet().Where(filter);
            totalRecords = dbset.Count();

            switch (order)
            {
                case Domain.Seedwork.Enumerators.Order.Ascending:
                    return dbset.OrderBy(orderByExpression)
                          .Skip(pageSize * pageIndex)
                          .Take(pageSize);

                case Domain.Seedwork.Enumerators.Order.Descending:
                    return dbset.OrderByDescending(orderByExpression)
                          .Skip(pageSize * pageIndex)
                          .Take(pageSize);

                default:
                    return dbset.Skip(pageSize * pageIndex)
                          .Take(pageSize);
            }
        }

        public IEnumerable<TAggregateRoot> GetFilterPaged<KProperty>(ISpecification<TAggregateRoot> specification, Expression<Func<TAggregateRoot, KProperty>> orderByExpression, HST.Domain.Seedwork.Enumerators.Order order, int pageIndex, int pageSize, out int totalRecords)
        {
            var dbset = GetSet().Where(specification.SatisfiedBy());
            totalRecords = dbset.Count();

            switch (order)
            {
                case Domain.Seedwork.Enumerators.Order.Ascending:
                    return dbset.OrderBy(orderByExpression)
                          .Skip(pageSize * pageIndex)
                          .Take(pageSize);

                case Domain.Seedwork.Enumerators.Order.Descending:
                    return dbset.OrderByDescending(orderByExpression)
                          .Skip(pageSize * pageIndex)
                          .Take(pageSize);

                default:
                    return dbset.Skip(pageSize * pageIndex)
                          .Take(pageSize);
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// <see cref="M:System.IDisposable.Dispose"/>
        /// </summary>
        public void Dispose()
        {
            if (_databaseFactory != null)
            {
                _databaseFactory.Dispose();
            }
        }

        #endregion
    }
}
